﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using FreeRedis;
using UserService.Application.Constract.DTO.Input;
using UserService.Application.Constract.DTO.Output;
using UserService.Application.Constract.IAppService;
using UserService.Domain.Repository;
using Volo.Abp.Application.Services;
using LeoGemini.Service.Infrastructure.Extension;
using LeoGemini.Service.Infrastructure.Handler.Exception;
using LeoGemini.Service.Infrastructure.Security.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using UserService.Application.Constract;
using UserService.Domain.Aggregate.Address;
using UserService.Domain.Aggregate.User;
using UserService.Domain.Event;
using UserService.Domain.Service;
using UserService.Domain.Specification;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Specifications;

namespace UserService.Application.AppService
{
    /// <summary>
    /// 用户应用服务
    /// </summary>
    public class UserAppService : ApplicationService, IUserAppService
    {
        private readonly IUserRepository _userRepository;
        private readonly IAddressRepository _addressRepository;
        private readonly IUserManager _userManager;
        private readonly RedisClient _cache;
        private readonly IDistributedEventBus _distributedEventBus;
        private readonly IVerifyCodeManager _verifyCodeManager;
        private readonly ITokenBuilder _tokenBuilder;

        public UserAppService(
            IUserRepository userRepository,
            IAddressRepository addressRepository,
            IUserManager userManager,
            RedisClient cache,
            IDistributedEventBus distributedEventBus,
            IVerifyCodeManager verifyCodeManager,
            ITokenBuilder tokenBuilder)
        {
            _userRepository = userRepository;
            _addressRepository = addressRepository;
            _userManager = userManager;
            _cache = cache;
            _distributedEventBus = distributedEventBus;
            _verifyCodeManager = verifyCodeManager;
            _tokenBuilder = tokenBuilder;
        }
        
        /// <summary>
        /// 用户注册
        /// </summary>
        public async Task<CreateUserReturnDto> CreateUserAsync(CreateUserInputDto dto)
        {
            // 规约获取用户
            var user = await _userRepository.SingleOrDefaultAsync(
                new CheckUserExistsSpecification(dto.UserName)
                    .Or(new CheckUserExistsSpecification(dto.PhoneNum))
                    .Or(new CheckUserExistsSpecification(dto.Email))
                    .ToExpression());
            
            // 用户已存在
            if (user != null)
            {
                // 抛出异常
                if (user.UserName == dto.UserName) 
                    throw new ConflictException(ErrorMessage.USER_USERNAME_EXIST);
                else if (user.PhoneNum == dto.PhoneNum) 
                    throw new ConflictException(ErrorMessage.USER_PHONENUM_EXIST);
                else if (user.Email == dto.Email) 
                    throw new ConflictException(ErrorMessage.USER_EMAIL_EXIST);
            }

            // 创建用户
            user = _userManager.CreateUser(
                dto.UserName, dto.Email, dto.PhoneNum, dto.Password, dto.BirthDate, dto.AllowedNotification);
            user.ExtraProperties["Role"] = "User";
            await _userRepository.InsertAsync(user);

            return ObjectMapper.Map<User, CreateUserReturnDto>(user);
        }

        /// <summary>
        /// 用户登陆
        /// </summary>
        public async Task<UserLoginOutputDto> UserLoginAsync(UserLoginInputDto dto)
        {
            // 规约获取用户
            var user = await _userRepository.SingleOrDefaultAsync(
                new CheckUserExistsSpecification(dto.Account)
                    .ToExpression());

            // 用户名不存在
            if (user == null)
                throw new NotFoundException(ErrorMessage.USER_USERNAME_NOT_EXIST);

            // 邮箱未校验
            if (!user.IsEmailConfirmed && user.Email == dto.Account)
                throw new UnauthorizedException(ErrorMessage.USER_EMAIL_NOT_TRUSTED);
            
            // 手机号码未校验
            if (!user.IsPhoneNumConfirmed && user.PhoneNum == dto.Account)
                throw new UnauthorizedException(ErrorMessage.USER_PHONE_NOT_TRUSTED);
            
            // 密码错误
            if ((user.Salt + dto.Password).ToMd5() != user.Password)
                throw new BadRequestException(ErrorMessage.USER_PASSWORD_ERROR);

            // 颁发token
            var claims = new Claim[]
            {
                new(ClaimTypes.Name, user.UserName),
                new(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new(ClaimTypes.Role, (user.ExtraProperties.ContainsKey("Role") ? user.ExtraProperties["Role"].ToString() : "User") ?? "User")
            };

            return new UserLoginOutputDto{ Token =  _tokenBuilder.BuildJwtToken(claims).TokenValue};
        }

        /// <summary>
        /// 获取用户详情
        /// </summary>
        public async Task<CreateUserReturnDto> GetUserByUserIdAsync(Guid userId)
        {
            // 缓存or数据库获取用户
            var user = await _cache.CacheOrGetAsync($"user:{userId}",
                async () => await _userRepository.FindAsync(userId));

            // 用户不存在
            if (user == null)
                throw new NotFoundException(ErrorMessage.USER_NO_EXIST);

            return ObjectMapper.Map<User, CreateUserReturnDto>(user);
        }

        /// <summary>
        /// 修改用户详情
        /// </summary>
        public async Task<CreateUserReturnDto> EditUserByUserIdAsync(Guid userId, EditUserInputDto dto)
        {
            // 用户没有权限
            if (userId != dto.UserId)
                throw new UnauthorizedException(ErrorMessage.USER_HAVE_NOT_PERMISSION);

            var cacheKey = $"user:{userId}";
            // 获取用户
            var user = await _cache.CacheOrGetAsync(cacheKey,
                async () => await _userRepository.FindAsync(userId));
                
            // 用户不存在
            if (user == null)
                throw new NotFoundException(ErrorMessage.USER_NO_EXIST);

            user.ChangeProfile(dto.Intro, dto.Avatar, dto.Sex, dto.BirthDate);
            // 持久化
            await _cache.RemoveAndPersistAsync(
                async () =>
                {
                    // 获取context
                    var dbContext = await _userRepository.GetDbContextAsync();
                    // 标记实体变更
                    dbContext.Entry(user.Profile).State = EntityState.Modified;
                    await _userRepository.UpdateAsync(user);
                }, cacheKey);

            return ObjectMapper.Map<User, CreateUserReturnDto>(user);
        }

        /// <summary>
        /// 用户获取配送地址信息
        /// </summary>
        public async Task<ICollection<CreateAddressReturnDto>> GetAddressesByUserIdAsync(Guid userId)
        {
            var cacheKey = $"user:{userId}:addresses";
            var addresses = await _cache.CacheOrGetAsync(cacheKey,
                async () => await _addressRepository.Where(address => address.ReferenceUserId == userId)
                    .ToListAsync());

            return ObjectMapper.Map<ICollection<Address>, ICollection<CreateAddressReturnDto>>(addresses);
        }

        /// <summary>
        /// 用户新增配送地址信息
        /// </summary>
        public async Task<CreateAddressReturnDto> CreateAddressAsync(Guid userId, CreateAddressInputDto dto)
        {
            // 用户没有权限
            if (userId != dto.ReferenceUserId)
                throw new UnauthorizedException(ErrorMessage.USER_HAVE_NOT_PERMISSION);

            // 创建用户配送地址
            var address = new Address(
                GuidGenerator.Create(), dto.ReferenceUserId, dto.Contact, dto.PhoneNum, dto.Province, dto.City,
                dto.Region, dto.Details);
            
            // 持久化
            await _addressRepository.InsertAsync(address);

            return ObjectMapper.Map<Address, CreateAddressReturnDto>(address);
        }

        /// <summary>
        /// 用户修改配送地址信息
        /// </summary>
        public async Task<CreateAddressReturnDto> EditAddressAsync(Guid userId, Guid addressId, EditAddressInputDto dto)
        {
            // 用户没有权限
            if (userId != dto.ReferenceUserId)
                throw new UnauthorizedException(ErrorMessage.USER_HAVE_NOT_PERMISSION);
            
            // 配送地址id不一致
            if (addressId != dto.Id)
                throw new BadRequestException(ErrorMessage.ADDRESS_COMPARE_ERROR);
            
            var cacheKeys = new[] {$"user:{userId}:address:{addressId}", $"user:{userId}:addresses"};
            var address = await _addressRepository.FindAsync(addressId);
            // 变更地址
            address.Migrate(
                dto.Contact, dto.PhoneNum, dto.Province, dto.City, dto.Region, dto.Details);

            // 持久化
            await _cache.RemoveAndPersistAsync(
                async () =>
                {
                    // 获取context
                    var dbContext = await _userRepository.GetDbContextAsync();
                    // 标记实体变更
                    dbContext.Entry(address).State = EntityState.Modified;
                    await _addressRepository.UpdateAsync(address);
                }, cacheKeys);

            return ObjectMapper.Map<Address, CreateAddressReturnDto>(address);
        }

        /// <summary>
        /// 用户删除配送地址信息
        /// </summary>
        public async Task DeleteAddressAsync(Guid userId, Guid addressId)
        {
            var cacheKeys = new[] {$"user:{userId}:address:{addressId}", $"user:{userId}:addresses"};
            await _cache.RemoveAndPersistAsync(
                async () =>
                {
                    await _addressRepository.DeleteAsync(addressId);
                }, cacheKeys);
        }

        /// <summary>
        /// 通过用户id和配送地址id获取配送地址信息
        /// </summary>
        public async Task<CreateAddressReturnDto> GetAddressByUserIdAndAddressIdAsync(Guid userId, Guid addressId)
        {
            var cacheKey = $"user:{userId}:address:{addressId}";

            // 缓存查询
            var address = await _cache.CacheOrGetAsync(cacheKey,
                async () => await _addressRepository.FindAsync(addressId));

            return ObjectMapper.Map<Address, CreateAddressReturnDto>(address);
        }

        /// <summary>
        /// 用户请求验证邮箱
        /// </summary>
        public async Task PublishUserRequireToTrustEmailEventAsync(Guid userId)
        {
            // 获取用户
            var user = await GetUserByUserIdAsync(userId);
            
            // 分发事件
            await _distributedEventBus.PublishAsync(new RequireToTrustEmailEvent
            {
                UserId = userId,
                Email = user.Email,
                UserName = user.UserName,
                VerifyCode = _verifyCodeManager.GenerateVerifyCode(userId, VerifyAction.VERIFY_EMAIL)
            });
        }

        /// <summary>
        /// 用户确认邮箱
        /// </summary>
        public async Task RequireToTrustedEmailAsync(Guid userId, ConfirmMailInputDto dto)
        {
            // 用户没有权限
            if (userId != dto.UserId)
                throw new UnauthorizedException(ErrorMessage.USER_HAVE_NOT_PERMISSION);
            
            // 获取对应用户的验证码
            var verifyCode = _verifyCodeManager
                .GetGeneratedVerifyCode(userId, VerifyAction.VERIFY_EMAIL);

            // 用户id没有对应验证请求
            if (string.IsNullOrEmpty(verifyCode))
                throw new NotFoundException(ErrorMessage.USER_DONT_HAVE_THIS_REQUEST);

            // 验证码不匹配
            if (verifyCode != dto.VerifyCode)
                throw new BadRequestException(ErrorMessage.USER_VERIFY_DO_NOT_COMPARE);

            // 删除验证码
            _verifyCodeManager.DestroyGeneratedVerifyCode(userId, VerifyAction.VERIFY_EMAIL);
            // 修改用户
            var cacheKey = $"user:{userId}";
            var user = await _cache.CacheOrGetAsync(cacheKey,
                async () => await _userRepository.FindAsync(userId));
            user.ConfirmEmail();
            
            // 持久化
            await _cache.RemoveAndPersistAsync(
                async () =>
                {
                    await _userRepository.UpdateAsync(user);
                }, cacheKey);
        }

        /// <summary>
        /// 用户请求验证手机号
        /// </summary>
        public async Task PublishUserRequireToTrustPhoneNumEventAsync(Guid userId)
        {
            // 获取用户
            var user = await GetUserByUserIdAsync(userId);
            
            // 分发事件
            await _distributedEventBus.PublishAsync(new RequireToTrustPhoneEvent
            {
                UserId = userId,
                Phone = user.PhoneNum,
                UserName = user.UserName,
                VerifyCode = _verifyCodeManager.GenerateVerifyCode(userId, VerifyAction.VERIFY_PHONE)
            });
        }
    }
}